﻿using System;
using System.Data;
using System.Text;
using System.Collections.Generic;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
    #region Documentation.
    /// <summary>
    /// Provides static methods that create aggregate functions.
    /// </summary>
    /// <remarks><see cref="AggregateFunction"/> cannot be created by using its constructor. The factory class must be used 
    /// to create instances of supported aggregate functions.
    /// <seealso cref="AggregateFunction"/>
    /// <seealso cref="SelectStatement"/></remarks>
    /// <example>
    /// The following example retrieves the number of employees hired before January 1, 2000.
    /// <code>
    /// public int CountEmployeesHiredBefore2000()
    /// {
    /// 	EmployeesMeta employees = new EmployeesMeta();
    /// 	SelectStatement select = new SelectStatement(employees);
    /// 	select.SelectList.Add(AggregateFunctionFactory.Count("hiredBefore2000Count"));
    /// 	select.Where.Add(PredicateFactory.Compare(employees.HireDate, "&lt;", new DateTime(2000, 1, 1)));
    /// 
    /// 	// Execute method returns a DataTable that contains one row.
    /// 	// Alias given to the aggregate function is the name of the column which holds the function result.
    /// 	DataTable data = select.Execute();
    /// 	int hiredBefore2000Count = (int) data.Rows[0]["hiredBefore2000Count"];
    /// 	return hiredBefore2000Count;
    /// }
    /// </code>
    /// </example>	
    #endregion
    public static class AggregateFunctionFactory
    {
        // As a conveniance, functions paremeters/items are internaly represented as instances of PredicateItem class.
        // It is expected that all DBMSs render predicate and function items in the same manner.

        #region AVG.

        /// <summary>
        /// Returns the average of the values in a group.
        /// </summary>
        /// <param name="field">Field. Null is not allowed.</param>
        /// <param name="distinct">Specifies that AVG be performed only on each unique instance of a value, regardless of how many times the value occurs.</param>
        /// <param name="alias">Function alias. May be null.</param>
        /// <returns>AVG function.</returns>
        public static AggregateFunction Avg(IDbColumn field, bool distinct, string alias)
        {
            if (field == null)
                throw new ArgumentNullException("field", Messages.AggregateFunctionFactory_FieldMayNotBeNull);

            List<IRenderSql> functionParams = new List<IRenderSql>();
            functionParams.Add(new PredicateItem(field, SqlItemType.Column));
            return new AggregateFunction("AVG", distinct, alias, functionParams, field.DataType);
        }

        #endregion

        #region COUNT.

        /// <summary>
        /// Returns the number of items in a group.
        /// </summary>
        /// <param name="field">Field. Null is not allowed.</param>
        /// <param name="distinct">Specifies that COUNT returns the number of unique nonnull values.</param>
        /// <param name="alias">Function alias. May be null.</param>
        /// <returns>COUNT function.</returns>
        public static AggregateFunction Count(IDbColumn field, bool distinct, string alias)
        {
            if (field == null)
                throw new ArgumentNullException("field", Messages.AggregateFunctionFactory_FieldMayNotBeNull);

            List<IRenderSql> functionParams = new List<IRenderSql>();
            functionParams.Add(new PredicateItem(field, SqlItemType.Column));
            return new AggregateFunction("COUNT", distinct, alias, functionParams, typeof(int));
        }

        /// <summary>
        /// Returns the number of rows retrieved.
        /// </summary>
        /// <param name="alias">Function alias. May be null.</param>
        /// <returns>COUNT function.</returns>
        public static AggregateFunction Count(string alias)
        {
            List<IRenderSql> functionParams = new List<IRenderSql>();
            functionParams.Add(new PredicateItem("*", SqlItemType.FreeText));
            return new AggregateFunction("COUNT", false, alias, functionParams, typeof(int));
        }

        #endregion

        #region MAX.

        /// <summary>
        /// Returns the maximum value in the given column.
        /// </summary>
        /// <param name="field">Column. Null is not allowed.</param>
        /// <param name="alias">Function alias. May be null.</param>
        /// <returns>MAX funcion.</returns>
        public static AggregateFunction Max(IDbColumn field, string alias)
        {
            if (field == null)
                throw new ArgumentNullException("field", Messages.AggregateFunctionFactory_FieldMayNotBeNull);

            List<IRenderSql> functionParams = new List<IRenderSql>();
            functionParams.Add(new PredicateItem(field, SqlItemType.Column));
            return new AggregateFunction("MAX", false, alias, functionParams, field.DataType);
        }

        #endregion

        #region MIN.

        /// <summary>
        /// Returns the minimum value in the given column.
        /// </summary>
        /// <param name="field">Column. Null is not allowed.</param>
        /// <param name="alias">Function alias. May be null.</param>
        /// <returns>MIN funcion.</returns>
        public static AggregateFunction Min(IDbColumn field, string alias)
        {
            if (field == null)
                throw new ArgumentNullException("field", Messages.AggregateFunctionFactory_FieldMayNotBeNull);

            List<IRenderSql> functionParams = new List<IRenderSql>();
            functionParams.Add(new PredicateItem(field, SqlItemType.Column));
            return new AggregateFunction("MIN", false, alias, functionParams, field.DataType);
        }

        #endregion

        #region SUM.

        /// <summary>
        /// Returns the sum of all the values, or only the DISTINCT values, in the given column. SUM can be used with numeric columns only. Null values are ignored.
        /// </summary>
        /// <param name="field">Column. Null is not allowed.</param>
        /// <param name="distinct">Specifies that SUM returns the sum of unique values.</param>
        /// <param name="alias">Function alias. May be null.</param>
        /// <returns>SUM function.</returns>
        public static AggregateFunction Sum(IDbColumn field, bool distinct, string alias)
        {
            if (field == null)
                throw new ArgumentNullException("field", Messages.AggregateFunctionFactory_FieldMayNotBeNull);

            List<IRenderSql> functionParams = new List<IRenderSql>();
            functionParams.Add(new PredicateItem(field, SqlItemType.Column));
            return new AggregateFunction("SUM", distinct, alias, functionParams, field.DataType);
        }

        #endregion
    }
}